module mbox.Db;

import 
	mbox.Types;

struct MyBox {
	static const TableDef def	=
		{
			"mail_box", 
			[

				{DataType.TEXT,	"sFrom",		ColType.NULL },
				{DataType.TEXT,	"sTo",		ColType.NULL },
				{DataType.BLOB,		"sBody",		ColType.NULL },
				
				{DataType.UINT,		"isDone",		ColType.NULL },
				{DataType.UINT,		"_start",		ColType.NULL },
				{DataType.UINT,		"_last",		ColType.NULL },
				{DataType.UINT,		"_next",		ColType.NULL },
				{DataType.UINT,		"_trys",		ColType.NULL },
			]
		};
	
	mixin DefTable!(MyBox);

	mixin DefMask!(MyBox,  
			"all", 	 0xfff 
		);

	alias MyBox.Row_all Row ;
	alias RowSelect!(Row, "isDone < 1 AND _trys < ? AND _next < ? ORDER BY _last ASC, _start ASC LIMIT ?, ?", int, int,  int, int) SelAll;

	static Connection*	_conn ;
	static Mutex		mu ;
	static Semaphore	sem ;
	static Atomic!(bool)	isRunning ;

	static void Init() {
		
		_conn	= Connection.open( MyConf.AppDb );
		_conn.exec("PRAGMA journal_mode = MEMORY");
		_conn.exec(MyBox.sql_create ) ;
		
		mu	= new Mutex;
		sem	= new Semaphore ;
		
		isRunning.store(true) ;
	}
	
	static void push(Smtp_Reader* tel) {
		Row it ;
		it.sFrom	= tel._from ;
		it.sTo	= Txt.join( tel._to, "\n") ;
		it.sBody	= tel._body.slice ;
		it._start	= Clock.now.unix.seconds ;
		
		synchronized( mu ) {
			scope _stmt	= Statement.Create(_conn);
			scope(exit){
				delete _stmt ;
			}
			//_conn.begin();
			Row.Insert(_stmt, &it);
			//_conn.commit();
		}
		sem.notify;
	}
	
	static Row* peek(){
		Row* p	= new Row;
		synchronized( mu ) {
			scope _stmt	= Statement.Create(_conn);
			scope(exit){
				delete _stmt ;
			}
			uint now	=  Clock.now.unix.seconds ;
			SelAll.select(_stmt,  MyConf.try_max , now, 0, 1);
			if( !SelAll.fetchOne(_stmt, p) ){
				return null ;
			}
			p.sFrom	= p.sFrom.dup ;
			p.sTo		= p.sTo.dup ;
			p.sBody	= p.sBody.dup ;
		}
		return p ;
	}
	
	static void update(Row*  p){
		synchronized( mu ) {
			scope _stmt	= Statement.Create(_conn);
			scope(exit){
				delete _stmt ;
			}
			//_conn.begin();
			Row.Update(_stmt, p);
			//_conn.commit();
		}
	}
	
	static void dump() {
		synchronized( mu ) {
			scope _stmt	= Statement.Create(_conn) ;
			SelAll.select(_stmt, MyConf.try_max , 0, 0, 30);
			Row p;
			while( SelAll.fetchOne(_stmt, &p) ){
				Stdout.formatln("Time:{} From:{}, To:{} DATA:\n{}", p._start, p.sFrom, p.sTo, cast(char[]) p.sBody );
			}
			Stdout.flush ;
		}
	}
}


